/****************************************
* James Gomez                           *
* Project 3: Target Practice P3         *
* Comp 465                              *
* Professor Barnes                      *
* 12/7/2012                             *
*****************************************/

#ifndef _VECTOR_3D_
#define _VECTOR_3D_

#include "GomezJamesP3_OpenGL.h"

#include <math.h>

/*****************************************************************************
Simple class for describing and manipulating 3D vectors within the model view
******************************************************************************/
class Vector3D{

private:
	GLdouble x;
	GLdouble y;
	GLdouble z;

public:
	Vector3D(const GLdouble x, const GLdouble y, const GLdouble z);
	~Vector3D(){}

	inline void setX(const GLdouble x){ this->x = x; }
	inline void setY(const GLdouble y){ this->y = y; }
	inline void setZ(const GLdouble z){ this->z = z; }
	inline GLdouble getX() const { return x; }
	inline GLdouble getY() const { return y; }
	inline GLdouble getZ() const { return z; }

	void setComponents(const GLdouble x, const GLdouble y, const GLdouble z);
	void getComponents(GLdouble &x, GLdouble &y, GLdouble &z) const;

	void add(const Vector3D);
	void subtract(const Vector3D);
	void scale(const GLdouble scaleFactor);
	GLdouble dotProduct(const Vector3D) const;
	Vector3D crossProduct(const Vector3D) const;
	GLdouble magnitude() const;
	GLdouble distance(Vector3D v) const;
	GLboolean isEqual(const Vector3D) const;
	Vector3D getNegatedV() const;
	void negate();
	Vector3D getNormalizedV() const;
	void normalize();

};


inline Vector3D::Vector3D(const GLdouble x, const GLdouble y, const GLdouble z){
	setComponents(x,y,z);
}

inline void Vector3D::setComponents(const GLdouble x, const GLdouble y, const GLdouble z){
	this->x  = x;
	this->y  = y;
	this->z  = z;
}

inline void Vector3D::getComponents(GLdouble &x, GLdouble &y, GLdouble &z) const{
	x = this->x;
	y = this->y;
	z = this->z;
}

void Vector3D::add(const Vector3D v){
	x += v.getX();
	y += v.getY(); 
	z += v.getZ();
}

void Vector3D::subtract(const Vector3D v){
	x -= v.getX();
	y -= v.getY(); 
	z -= v.getZ();
}

inline void Vector3D::scale(const GLdouble scaleFactor){
	x *= scaleFactor;
	y *= scaleFactor;
	z *= scaleFactor;
}

inline GLdouble Vector3D::dotProduct(const Vector3D v) const{
	return (GLdouble) (x*v.getX()) + (y*v.getY()) + (z*v.getZ());
}

//this vector crossed with v, i.e. if this vector is u, then u x v
inline Vector3D Vector3D::crossProduct(const Vector3D v) const{
	GLdouble x1 = x, y1 = y, z1 = z;
	GLdouble x2 = v.getX(), y2 = v.getY(), z2 = v.getZ();

	return Vector3D( (y1*z2 - y2*z1), (x2*z1 - x1*z2), (x1*y2 - x2*y1) );
}

//euclidean distance of this vector from (0,0,0)
GLdouble Vector3D::magnitude() const{	
	return (GLdouble) sqrt(pow(x,2) + pow(y,2) + pow(z,2));
}

GLdouble Vector3D::distance(Vector3D v) const{
	return sqrt( 
		pow(v.getX() - x, 2) + 
		pow(v.getY() - y, 2) + 
		pow(v.getZ() - z, 2) 
	);
}

inline GLboolean Vector3D::isEqual(const Vector3D v) const{	

	if (x != v.getX())
		return false;

	if (y != v.getY())
		return false;

	if (z != v.getZ())
		return false;

		return true;
}

Vector3D Vector3D::getNegatedV() const{
	return Vector3D(-x, -y, -z);
}

void Vector3D::negate(){
	x = -x;
	y = -y;
	z = -z;
}

//get a normalized copy of this vector
Vector3D Vector3D::getNormalizedV() const{
	GLdouble mag = magnitude();

	return Vector3D(x/mag, y/mag, z/mag);
}

//This operation can't be undone without storing original coordinate values. Be careful.
void Vector3D::normalize(){
	GLdouble mag = magnitude();

	x = x/mag;
	y = y/mag;
	z = z/mag;
}


#endif